﻿#include "redis_db_connection_pool.h"
#include "redis_db_connection.h"

#include "log.h"

const char LOG_NAME[] = "CRedisDbConnectionPool";

CRedisDbConnectionPool::CRedisDbConnectionPool(void)
{
}

CRedisDbConnectionPool::~CRedisDbConnectionPool(void)
{
	clear();
}


bool CRedisDbConnectionPool::create( size_t szCount , const std::string& host , const std::string& port_or_socket , const std::string& user , const std::string& password , const std::string& database )
{


	m_host				= host;
	m_port_or_socket	= port_or_socket;
	m_user				= user;
	m_password			= password;
	m_database			= database;


	for (size_t t = 0; t < szCount; ++t)
	{
		CRedisDbConnection * pConn = new CRedisDbConnection;
		if (nullptr != pConn)
		{
			if (!pConn->Connect(host,port_or_socket,user,password,database))
			{
				LOG_FATAL("[CRedisDbConnectionPool::create], create CRedisDbConnectionPool failed!");
				return false;
			}

			pool_.push_back(ConnectionInfo(pConn));
		}
	}

	return (pool_.size() != 0);

}


CRedisDbConnection * CRedisDbConnectionPool::exchange( const CRedisDbConnection* pc )
{
	remove(pc);
	return grab();
}

CRedisDbConnection * CRedisDbConnectionPool::grab()
{

	//todo add lock	                             // ensure we're not interfered with

	if (CRedisDbConnection* mru = _find_mru())
	{
		return mru;
	}
	else
	{
		
		CRedisDbConnection* pConn = new CRedisDbConnection;
		//try
		{
			bool b = pConn->Connect(m_host, m_port_or_socket, m_user, m_password, m_database);
			if (!b)
			{
				LOG_FATAL("[CRedisDbConnectionPool::grab()], connect to redis failed!");
			}

			pool_.push_back(ConnectionInfo(pConn));
			pool_.back().in_use = true;

			return pool_.back().conn;
		}
	}

	return 0;
}


void CRedisDbConnectionPool::release( const CRedisDbConnection* pc )
{
	//todo add lock 	// ensure we're not interfered with

	for (PoolIt it = pool_.begin(); it != pool_.end(); ++it)
	{
		if (it->conn == pc)
		{
			it->in_use = false;
			it->last_used = time(0);
			break;
		}
	}
}

void CRedisDbConnectionPool::remove( const CRedisDbConnection* pc )
{
	//todo add lock	// ensure we're not interfered with

	for (PoolIt it = pool_.begin(); it != pool_.end(); ++it)
	{
		if (it->conn == pc)
		{
			_remove(it);
			return;
		}
	}
}

CRedisDbConnection * CRedisDbConnectionPool::safe_grab()
{
	CRedisDbConnection* pc;
	
	pc = grab();

	return pc;
}

void CRedisDbConnectionPool::clear( bool all /*= true*/ )
{
	//add lock	// ensure we're not interfered with

	PoolIt it = pool_.begin();
	while (it != pool_.end())
	{
		if (all || !it->in_use)
		{
			_remove(it++);
		}
		else
		{
			++it;
		}
	}
}

CRedisDbConnection* CRedisDbConnectionPool::_find_mru()
{
	PoolIt mru = std::max_element(pool_.begin(), pool_.end());
	if (mru != pool_.end() && !mru->in_use)
	{
		mru->in_use = true;
		return mru->conn;
	}
	else 
	{
		return 0;
	}
}

void CRedisDbConnectionPool::_remove( const PoolIt& it )
{
	delete (it->conn);
	it->conn = 0;
	pool_.erase(it);
}

void CRedisDbConnectionPool::KeepAlive( int iS )
{
	//todo: add lock	// ensure we're not interfered with

	for (PoolIt it = pool_.begin(); it != pool_.end(); ++it)
	{
		if (it->conn)
			it->conn->KeepAlive(iS);
	}
}
